import { ArkFile } from "arkanalyzer";
import { IssueReport } from "../../model/Defects";
import { Engine } from "../../model/Engine";
import { FixResult, RuleFix } from "../../model/Fix";
import Logger, { LOG_MODULE_TYPE } from "arkanalyzer/lib/utils/logger";
import { FixUtils } from "../../utils/common/FixUtils";

const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'EsLintFixEngine');
const BOM = '\uFEFF';


export class EsLintFixEngine implements Engine {
    
    applyFix(arkFile: ArkFile, issues: IssueReport[]): FixResult | undefined {
        let sourceText = arkFile.getCode();
        const remainIssues: IssueReport[] = [],
        fixIssues: IssueReport[] = [],
        bom = sourceText.startsWith(BOM) ? BOM : "",
        text = bom ? sourceText.slice(1) : sourceText;
        let lastPos = Number.NEGATIVE_INFINITY, output = bom;

        issues.forEach((issue) => {
            let fix = issue.fix;
            if (fix === undefined) {
                return;
            }
            if (!FixUtils.isRuleFix(fix)) {
                return;
            }
            if (fix.range[0] <= fix.range[1] && fix.range[1] !== 0) {
                fixIssues.push(issue);
            } else {
                remainIssues.push(issue);
            }
        });

        if (fixIssues.length) {
            for(const issue of fixIssues.sort(this.compareIssueByRange)) {
                let fix = issue.fix;
                if (fix === undefined) {
                    return;
                }
                if (!FixUtils.isRuleFix(fix)) {
                    return;
                }
                const start = fix.range[0];
                const end = fix.range[1];

                if (lastPos >= start || start > end) {
                    remainIssues.push(issue);
                }

                if ((start < 0 && end >= 0) || (start === 0 && fix.text.startsWith(BOM))) {
                    output = '';
                }

                output += text.slice(Math.max(0, lastPos), Math.max(0, start));
                output += fix.text;
                lastPos = end;
                fix.fixed = true;
            }
            output += text.slice(Math.max(0, lastPos));
            return {remainIssues: remainIssues.sort(this.compareIssueByLocation), output: output, outpath: ''}
        }
        return {remainIssues: remainIssues, output: bom + text, outpath: ''};
    }

    private compareIssueByRange(issue1: IssueReport, issue2: IssueReport) {
        let fix1 = issue1.fix;
        let fix2 = issue2.fix;
        if (FixUtils.isRuleFix(fix1) && FixUtils.isRuleFix(fix2)) {
            return fix1.range[0] - fix2.range[0] || fix1.range[1] - fix2.range[1];
        } else {
            return 0;
        }
    }

    private compareIssueByLocation(a: IssueReport, b: IssueReport) {
        return a.defect.reportLine - b.defect.reportLine || a.defect.reportColumn - b.defect.reportColumn;
    }
}